#include "CH58x_common.h"
#include "zqm_global.h"
#include "zqm_module.h"
#include "hal_adc.h"


#define ADC_BUFF_SIZE 20

static uint8_t adc_ref_reset = 0;   // flag to clean ref sampling buffer
static uint8_t adc_ntc1_reset = 0;  // flag to clean ntc1 sampling buffer
static uint8_t adc_ntc2_reset = 0;  // flag to clean ntc1 sampling buffer
static uint8_t adc_vbat_reset = 0;  // flag to clean vbat sampling buffer
static short adc_data_ref = 0;        // record final ref value
static short adc_data_ntc1 = 0;       // record final ntc1 value
static short adc_data_ntc2 = 0;       // record final ntc1 value
static short adc_data_vbat = 0;       // record final vbat value

// 去掉最大两个数和最小两个数后，剩下的数的平均值
static short AdcAverage2(short* adc, int n)
{
    int s = 0;
    short min1, min2, max1, max2;
    min1 = max1 = adc[0];
    min2 = max2 = adc[1];
    for (int i = 2; i < n; i++) {
        s += adc[i];

        if (min1 > adc[i]) {
            min2 = min1; min1 = adc[i];
        }
        else if (min2 > adc[i]) min2 = adc[i];

        if (max1 < adc[i]) {
            max2 = max1;  max1 = adc[i];
        }
        else if (max2 < adc[i]) max2 = adc[i];
    }

    return (short)((s + adc[0] + adc[1] - min1 - min2 - max1 - max2) / (n - 4));
}

#ifdef ZQ_SIMULATOR
// 读取参考电压-ADC_CH_VDD33
static short AdcReadRef(short calib)
{
    return simu_hw.adc[3];
}
// 读取Ntc1通道ADC采样电压
static int AdcReadNtc1(short calib)
{
    return simu_hw.adc[0];
}
// 读取Ntc2通道ADC采样电压
static int AdcReadNtc2(short calib)
{
    return simu_hw.adc[1];
}
// 读取VABT通道ADC采样电压 // 最多需要ADC转换:  5+1+3+5=14次
static int AdcReadVBat(short calib)
{
    return simu_hw.adc[2];
}
#else // no ZQ_SIMULATOR
// 模仿沁恒系统函数ADC_ExcutSingleConver，采样改单次为连续n次, if(n >= 3)去最大最小取平均，否则直接平均
// 连续采样除了考虑精度和平稳,也用来防止采样时电流小（比如电池电压），充电不足导致采样值偏低。
static short AdcExcuteNConver(int n)
{
    int sum = 0;
    short v = 0, max = 0, min = 0x7fff;
    for (int i = 0; i < n; i++) {
        R8_ADC_CONVERT = RB_ADC_START;
        while (R8_ADC_CONVERT & RB_ADC_START);
        v = (R16_ADC_DATA & RB_ADC_DATA);
        sum += v;
        if (min > v) min = v;
        if (max < v) max = v;
    }
    return (short)((n < 3) ? (sum / n) : ((sum - min - max) / (n - 2)));
}
// 采样值转为电压mv, value - 采样值; pag - 放大系数; calib - 校准值
static short AdcValue2Voltage(short value, ADC_SignalPGATypeDef pga, short calib)
{
    switch (pga) {
    case ADC_PGA_2: return (((int)(value + calib) * 1050 + 2048) / 4096 + 1050 / 2); // (ADC/4096+0.5)*Vref 
    case ADC_PGA_0: return (((int)(value + calib) * 1050 + 1024) / 2048);            // (ADC/2048)*Vref
    case ADC_PGA_1_2: return (((int)(value + calib) * 1050 +512) / 1024 - 1 * 1050); // (ADC/1024-1)*Vref
    default: return (((int)(value + calib) * 1050 + 256) / 512 - 3 * 1050);          // (ADC/512-3)*Vref
    }
}
// 读取参考电压-ADC_CH_VDD33, 调试时候这个函数应该读到实际电源电压才对。
static short AdcReadRef(short calib)
{
    ADC_InterBATSampInit();
    // 丢弃前5次, 取后5次去最大最小后平均. 测电源, PGA值大概是1/12, 太小, 抛弃前5次等待采样电容预充电, 
    AdcExcuteNConver(5);
    return AdcValue2Voltage(AdcExcuteNConver(5), ADC_PGA_1_4, calib);
}
// 读取Ntc1通道ADC采样电压 // 最多需要ADC转换:  1+1+4+5+5=16次, 调试时候这个函数应该读到传感器输入点实际电压值才对。
static short AdcReadNtc1(short calib)
{
    short adc_value, mv = 0;
    ADC_ChannelCfg(ADC_CH_NTC1); // 切换通道
    // 预设adc放大系数为ADC_PGA_2，根据得到的数值逐步判断PGA的增益范围, 收缩精度
    ADC_SignalPGATypeDef pga = ADC_PGA_2;
    ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
    // short Calib = ADC_DataCalib_Rough(); 
    adc_value = AdcExcuteNConver(1);
    mv = AdcValue2Voltage(adc_value, pga, calib);
    if (mv > 1500 || mv < 600) {
        pga = ADC_PGA_0;
        ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
        // Calib = ADC_DataCalib_Rough();
        adc_value = AdcExcuteNConver(1);
        mv = AdcValue2Voltage(adc_value, pga, calib);
    }
    if (mv > 2000) {
        pga = ADC_PGA_1_2;
        ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
        // Calib = ADC_DataCalib_Rough();
        adc_value = AdcExcuteNConver(1);
        mv = AdcValue2Voltage(adc_value, pga, calib);
    }
    if (mv > 3000) {
        pga = ADC_PGA_1_4;
        ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
        // Calib = ADC_DataCalib_Rough();
        adc_value = AdcExcuteNConver(1);
        AdcExcuteNConver(5); // 丢掉前5次, 用来防止采样时电流小（比如电池电压），采样电容充电不足.
    }
    // 正式读取
    adc_value = AdcExcuteNConver(5);
    mv = AdcValue2Voltage(adc_value, pga, calib);
	
	// if((zq_timer_50ms % 10000) == 0) // 每10s打印一次瞬时NTC1电压值
    //     ZQLOG("ADC read ntc1 data(ava of 5 num): %d", mv);
    return mv;
}
// 读取Ntc2通道ADC采样电压 // 最多需要ADC转换:  1+1+4+5+5=16次, 调试时候这个函数应该读到传感器输入点实际电压值才对。
static short AdcReadNtc2(short calib)
{
    short adc_value, mv = 0;
    ADC_ChannelCfg(ADC_CH_NTC2); // 切换通道
    // 预设adc放大系数为ADC_PGA_2，根据得到的数值逐步判断PGA的增益范围, 收缩精度
    ADC_SignalPGATypeDef pga = ADC_PGA_2;
    ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
    // short Calib = ADC_DataCalib_Rough(); 
    adc_value = AdcExcuteNConver(1);
    mv = AdcValue2Voltage(adc_value, pga, calib);
    if (mv > 1500 || mv < 600) {
        pga = ADC_PGA_0;
        ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
        // Calib = ADC_DataCalib_Rough();
        adc_value = AdcExcuteNConver(1);
        mv = AdcValue2Voltage(adc_value, pga, calib);
    }
    if (mv > 2000) {
        pga = ADC_PGA_1_2;
        ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
        // Calib = ADC_DataCalib_Rough();
        adc_value = AdcExcuteNConver(1);
        mv = AdcValue2Voltage(adc_value, pga, calib);
    }
    if (mv > 3000) {
        pga = ADC_PGA_1_4;
        ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
        // Calib = ADC_DataCalib_Rough();
        adc_value = AdcExcuteNConver(1);
        AdcExcuteNConver(5); // 丢掉前5次, 用来防止采样时电流小（比如电池电压），采样电容充电不足.
    }
    // 正式读取
    adc_value = AdcExcuteNConver(5);
    mv = AdcValue2Voltage(adc_value, pga, calib);
	
	// if((zq_timer_50ms % 10000) == 0) // 每10s打印一次瞬时NTC1电压值
    //     ZQLOG("ADC read ntc1 data(ava of 5 num): %d", mv);
    return mv;
}
// 读取VABT通道ADC采样电压 // 最多需要ADC转换:  5+1+3+5=14次
static short AdcReadVBat(short calib)
{
    int adc_value, mv = 0;
    ADC_ChannelCfg(ADC_CH_BATT); // 切换通道
    // 预设adc放大系数为ADC_PGA_1_4，根据得到的数值逐步判断PGA的增益范围, 收缩精度
    ADC_SignalPGATypeDef pga = ADC_PGA_1_4;
    ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
    AdcExcuteNConver(5); // 丢掉前5次, 用来防止采样时电流小（比如电池电压），采样电容充电不足.
    adc_value = AdcExcuteNConver(1);
    mv = AdcValue2Voltage(adc_value, pga, calib);
    if (mv < 2900) {
        pga = ADC_PGA_1_2;
        ADC_ExtSingleChSampInit(SampleFreq_3_2, pga);
    }
    // 正式读取
    adc_value = AdcExcuteNConver(5);
    mv = AdcValue2Voltage(adc_value, pga, calib);

	// if((zq_timer_50ms % 10000) == 0) // 每10s打印一次瞬时Battery电压值
    //     ZQLOG("ADC read Battery data(ava of 5 num): %d", mv);
    return mv;
}
#endif // ZQ_SIMULATOR


// ADC functions -------------------------------------------------------------------------------------------------------
void AdcResetRef(void)
{
    adc_ref_reset = 1;
}
void AdcSamplingRef(short calib)
{
    static uint8_t cur_pos = 0;             // cur data position in raw_data buffer
    static uint8_t eliminate_dither = 0;    // eliminate dither count
    static short raw_data[ADC_BUFF_SIZE];   // smooth window buffer

    if(cur_pos == ADC_BUFF_SIZE) cur_pos = 0; // 循环
    // read adc value to buff
    raw_data[cur_pos] = AdcReadRef(calib);
    if (adc_ref_reset == 1) {// reset后初次填充buffer
        for (int i = 0; i < ADC_BUFF_SIZE; i++)
            raw_data[i] = raw_data[cur_pos];
        adc_data_ref = raw_data[0];    // 初次直接取值，后续加平滑计算
        cur_pos = eliminate_dither = adc_ref_reset = 0;
    }
    // 平均和平滑算法
    else if (0 == cur_pos % 5) {  // 每5次做所有数值平均计算
        int average = (short)AdcAverage2(raw_data, ADC_BUFF_SIZE);
        if (average != adc_data_ref) { // 连续4次值变化，开始变动
            if ((++eliminate_dither) > 3)
                adc_data_ref = average;
        }
        else 
            eliminate_dither = 0;
    }
    cur_pos++;
}

void AdcResetNtc1(void)
{
    adc_ntc1_reset = 1;
}
void AdcResetNtc2(void)
{
    adc_ntc2_reset = 1;
}
void AdcSamplingNtc1(short calib)
{
    static uint8_t cur_pos = 0;             // cur data position in raw_data buffer
    static uint8_t eliminate_dither = 0;    // eliminate dither count
    static short raw_data[ADC_BUFF_SIZE];   // smooth window buffer

    if(cur_pos == ADC_BUFF_SIZE) cur_pos = 0; // 循环
    // read adc value to buff
    raw_data[cur_pos] = AdcReadNtc1(calib);
    if (adc_ntc1_reset == 1) {// reset后初次填充buffer
        for (int i = 0; i < ADC_BUFF_SIZE; i++)
            raw_data[i] = raw_data[cur_pos];
        adc_data_ntc1 = raw_data[0];    // 初次直接取值，后续加平滑计算
        cur_pos = eliminate_dither = adc_ntc1_reset = 0;
    }
    else if(raw_data[cur_pos] > 3200) {  // 传感器错误，不加平滑算法
        adc_data_ntc1 = raw_data[cur_pos];
    }
    // 平均和平滑算法
    else if (0 == cur_pos % 5) {  // 每5次做所有数值平均计算
        int average = AdcAverage2(raw_data, ADC_BUFF_SIZE);
        if (average != adc_data_ntc1) { // 连续4次值变化，开始变动
            if ((++eliminate_dither) > 3)
                adc_data_ntc1 = average;
        }
        else 
            eliminate_dither = 0;
    }
    cur_pos++;
}
void AdcSamplingNtc2(short calib)
{
    static uint8_t cur_pos = 0;             // cur data position in raw_data buffer
    static uint8_t eliminate_dither = 0;    // eliminate dither count
    static short raw_data[ADC_BUFF_SIZE];   // smooth window buffer

    if (cur_pos == ADC_BUFF_SIZE) cur_pos = 0; // 循环
    // read adc value to buff
    raw_data[cur_pos] = AdcReadNtc2(calib);
    if (adc_ntc2_reset == 1) {// reset后初次填充buffer
        for (int i = 0; i < ADC_BUFF_SIZE; i++)
            raw_data[i] = raw_data[cur_pos];
        adc_data_ntc2 = raw_data[0];    // 初次直接取值，后续加平滑计算
        cur_pos = eliminate_dither = adc_ntc2_reset = 0;
    }
    else if (raw_data[cur_pos] > 3200) {  // 传感器错误，不加平滑算法
        adc_data_ntc2 = raw_data[cur_pos];
    }
    // 平均和平滑算法
    else if (0 == cur_pos % 5) {  // 每5次做所有数值平均计算
        int average = AdcAverage2(raw_data, ADC_BUFF_SIZE);
        if (average != adc_data_ntc2) { // 连续4次值变化，开始变动
            if ((++eliminate_dither) > 3)
                adc_data_ntc2 = average;
        }
        else
            eliminate_dither = 0;
    }
    cur_pos++;
}
void AdcResetVBat(void)
{
    adc_vbat_reset = 1;
}
void AdcSamplingVBat(short calib)
{
    static uint8_t cur_pos = 0;             // cur data position in raw_data buffer
    static uint8_t eliminate_dither = 0;    // eliminate dither count
    static short raw_data[ADC_BUFF_SIZE];   // smooth window buffer

    if(cur_pos == ADC_BUFF_SIZE) cur_pos = 0; // 循环
    // read adc value to buff
    raw_data[cur_pos] = AdcReadVBat(calib);
    if (adc_vbat_reset == 1) {// reset后初次填充buffer
        for (int i = 0; i < ADC_BUFF_SIZE; i++)
            raw_data[i] = raw_data[cur_pos];
        adc_data_vbat = raw_data[0];    // 初次直接取值，后续加平滑计算
        cur_pos = eliminate_dither = adc_vbat_reset = 0;
    }
    // 平均和平滑算法
    else if (0 == cur_pos % 5) {  // 每5次做所有数值平均计算
        int average = AdcAverage2(raw_data, ADC_BUFF_SIZE);
        if (average != adc_data_vbat) { // 连续4次值变化，开始变动
            if ((++eliminate_dither) > 3)
                adc_data_vbat = average;
        }
        else 
            eliminate_dither = 0;
    }
    cur_pos++;
}

short AdcGetNtc1(void)
{
    return adc_data_ntc1;
}
short AdcGetNtc2(void)
{
    return adc_data_ntc2;
}
short AdcGetRef(void)
{
    return adc_data_ref;
}
short AdcGetVBat(void)
{
    return adc_data_vbat;
}
